﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using InfiniteRogue;

namespace InfiniteRogue.AI
{
    public class AI
    {
        protected Engine engine;
        protected Character character;

        public AI(Character c, Engine e)
        {
            character = c;
            engine = e;
        }

        public virtual void Turn()
        {

        }

        public Tuple<int, int> MoveToward(MapCoord destination)
        {
            return MoveToward(character.Pos, destination, engine);
        }

        public static Tuple<int, int> MoveToward(MapCoord start, MapCoord end, Engine engine)
        {
            int algoLimit = 1000;
            // create value for unsuccessful run of algorithm
            Tuple<int, int> defaultMove = Tuple.Create(0, 0);
            // handle edge case of not considering more than the first move
            if (start.x == end.x && start.y == end.y) return defaultMove;
            // points of consideration are sorted based on best guess at going in right direction
            VCSKicksCollection.PriorityQueue<PathFindMapCoord> moves = new VCSKicksCollection.PriorityQueue<PathFindMapCoord>();
            // must only consider each spot once
            HashSet<MapCoord> visited = new HashSet<MapCoord>();
            // get the loop started
            PathFindMapCoord startPathFind = new PathFindMapCoord(null, end, start);
            moves.Enqueue(startPathFind);
            visited.Add(startPathFind.Coord);
            // either all possible moves will be considered, or the destination will be found and the function will return
            while (moves.Count > 0 && algoLimit > 0)
            {
                --algoLimit;
                PathFindMapCoord taken = moves.Dequeue();
                foreach (var move in Utilities.Moves)
                {
                    if (!visited.Contains(taken.Coord + move))
                    {
                        PathFindMapCoord considered = new PathFindMapCoord(taken, end, taken.Coord + move);
                        if (considered.CompareTo(end) == 0)
                        {
                            // path find complete
                            // store the whole path just for kicks (for now)
                            List<Tuple<int, int>> path = new List<Tuple<int,int>>();
                            // walk back through the references to the original source
                            while(considered.Source != null)
                            {
                                // get the move from the source to the considered spot
                                path.Add(considered.Coord - considered.Source.Coord);
                                considered = considered.Source;
                            }
                            // the first move taken from the original source
                            return path.Last();
                        }
                        else
                        {
                            if (engine.IsSpotPassible(considered.Coord))
                            {
                                // consider this spot later
                                moves.Enqueue(considered);
                            }
                            // remove spot from future checks
                            visited.Add(considered.Coord);
                        }
                    }
                }
            }

            // all possible moves were considered and no paths were successful. So return default move.
            return defaultMove;
        }

    }

    public class PathFindMapCoord : IComparable
    {
        public PathFindMapCoord(PathFindMapCoord source, MapCoord end, MapCoord coord)
        {
            this.Coord = coord;
            this.Source = source;
            this.End = end;
        }

        public PathFindMapCoord Source { get; set; }
        public MapCoord End { get; set; }
        public MapCoord Coord { get; set; }

        public int CompareTo(object obj)
        {
            if (obj is PathFindMapCoord)
            {
                PathFindMapCoord other = (PathFindMapCoord)obj;
                return this.Coord.DistanceTo(End).CompareTo(other.Coord.DistanceTo(End));
            }
            else if (obj is MapCoord)
            {
                MapCoord other = (MapCoord)obj;
                return this.Coord.DistanceTo(End).CompareTo(other.DistanceTo(End));
            }
            else
            {
                throw new NotImplementedException();
            }
        }
    }

}


